-------------------------------------------------------------------------------
--
--  Spynesso's custom healing adding.
--
-------------------------------------------------------------------------------
Spy.media = LibStub("LibSharedMedia-3.0")
Spy.frames = {}
Spy.frameTypes = {}
Spy.startup = {} -- list of functions that will be called on startup
Spy.shutdown = {} -- """" on player logout

AddEventListener("ADDON_LOADED", function(self, addon)
   if addon == "SpyHud" then
      for k,v in pairs(Spy.savedVars) do
         InitSavedVar(k, v)
      end
      Spy.bLoaded = true
      Spy.CallStartupFuncs()
      return true
   end
end)

-------------------------------------------------------------------------------
local media = Spy.media
for k,v in pairs(media.MediaTable) do
   assert(type(k) == 'string' and k:len() > 1)
   local fname = (k:sub(1, 1)):upper() .. k:sub(2)
   _G[fname] = function(name)
      return media:Fetch(k, name)
   end
   printf('%s = Spy.media:Fetch("%s")', fname, k)
end

function Font(name) return Spy.media:Fetch('font', name) end

-----------------------------------------------------------------------
local WidgetTypes =
{
   "BUTTON", "CHECKBUTTON", "COLORSELECT", "COOLDOWN", 
   -- "DRESSUPMODEL", "EDITBOX",
   "FRAME", "MESSAGEFRAME", 
   -- "PLAYERMODEL", "SCROLLFRAME", 
   -- "SCROLLINGMESSAGEFRAME",
   "SLIDER", "STATUSBAR", 
}

for i, v in ipairs(WidgetTypes) do
   local f = CreateFrame(v)
   assert(f)
   Spy.frameTypes[v] =
   {
      name = v,
      proto = f,
      meta = {__index = f},
      ftype = v
   }
end

function Spy.NewFrameType(name, baseType)
   assert(Spy.frameTypes[name] == nil)
   local base = Spy.frameTypes[baseType]
   assert(base)
   local f = {}
   setmetatable(f, base.meta)
   Spy.frameTypes[name] =
   {
      name = name,
      proto = f,
      meta = {__index = f},
      ftype = base.ftype,
   }
   return f
end

function Spy.CreateFrame(typeName, ...)
   local t = Spy.frameTypes[typeName]
   assert(t)
   local f = CreateFrame(t.ftype, ...)
   setmetatable(f, t.meta)
   return f
end

-------------------------------------------------------------------------------
function Spy.SaveFramePoints(frame)
   local points = {}
   for i=1, frame:GetNumPoints() do
      local point, relativeTo, relativePoint, x, y = frame:GetPoint(i)
      local relativeName = nil
      if relativeTo then
         relativeName = relativeTo:GetName()
      end
      table.insert(points, {point, relativeName, relativePoint, x, y})
   end
   return points
end

function Spy.LoadFramePoints(frame, var)
   if frame and var then
      frame:ClearAllPoints()
      for i, v in ipairs(var) do
         frame:SetPoint(unpack(v))
      end
   end
end

function Spy.LoadFrame(frame)
   local var = SpyDB.Frames[frame:GetName()]
   if not var then
      ResetFrame(frame)
   else
      if var.bShown ~= nil then
         if var.bShown then
            frame:Show()
         else
            frame:Hide()
         end
      end
      Spy.LoadFramePoints(frame, var.points)
      if var.scale then frame:SetScale(var.scale) end
      if var.alpha then frame:SetAlpha(var.alpha) end
   end
end

function Spy.SaveFrame(frame)
   local var = {}
   var.points = Spy.SaveFramePoints(frame)
   var.scale = frame:GetScale()
   var.bShown = frame:IsShown()
   var.alpha = frame:GetAlpha()
   SpyDB.Frames[frame:GetName()] = var
end

function Spy.CreateAnchorFor(f)
   local anchor = CreateFrame("FRAME")
   anchor.tex = CreateTexture()
   anchor.tex:SetAllPoints()
   anchor.tex:SetTexture(1, 0, 0)
   anchor:SetWidth(10)
   anchor:SetHeight(10)
   anchor:SetPoint("BOTTOMRIGHT", UIParent, f:GetLeft(), f:GetTop())
   f:ClearAllPoints()
   f:SetPoint("TOPLEFT", anchor, "BOTTOMRIGHT")
   f.anchor = anchor
   -- f.SetPoint = function(self, ...)
      -- f.anchor:SetPoint(...)
      -- end
   return anchor
end

function Spy.CreateBackground(f, color, alpha, tex)
   local color = color or {0, 0, 0}
   local alpha = alpha or 0.5
   local tex = tex or color
   
   local b = f:CreateTexture(nil, 'BACKGROUND')
   b:SetAllPoints()
   b:SetTexture(tex)
   b:SetAlpha(alpha)
   return b
end

function Spy.CreateBox(name, w, h, color, alpha, point)
   local w, h = w or 100, h or 100
   local color = color or {0, 0, 0}
   local alpha = alpha or 1
   
   local f = CreateFrame('FRAME', name)
   if point then
      f:SetPoint(point)
   end
   f:SetWidth(w)
   f:SetHeight(h)
   f.tex = f:CreateTexture()
   f.tex:SetAllPoints()
   f.tex:SetTexture(unpack(color))
   f.tex:SetAlpha(alpha)
   
   return f
end

function Spy.CreateManagedFrame(ftype, name, width, height, movable, resizable)
   local f = CreateFrame(ftype, name)
   if point then
      f:SetPoint(point)
   end
   f:SetWidth(width or 10)
   f:SetHeight(height or 10)
   Spy.ManageFrame(f)
   MakeFrameDraggable(f, movable or true, resizable)
   f:Show()
   f.tex = f:CreateTexture()
   f.tex:SetAllPoints()
   f.tex:SetTexture(0, 0, 0)
   f.tex:SetAlpha(0.5)
   
   f.bLocked = false
   -- function f:ToggleLock()
      -- f.bLocked = not f.bLocked
      -- if bLocked then
         
      
   -- f:SetScript('OnMouseDown', function(self, button)
      -- if button=='LeftButton' and IsAltKeyDown() then
         -- f:ToggleLock()
      -- end)
   
   return f
end

function Spy.ManageFrame(frame)
   assert(Spy.bLoaded)
   Spy.frames[frame] = frame
   Spy.LoadFrame(frame)
end

function Spy.AddStartupFunc(func)
   tinsert(Spy.startup, func)
end

function Spy.CallStartupFuncs()
   for i, f in ipairs(Spy.startup) do
      f()
   end
end

AddEventListener("PLAYER_LOGOUT", function()
   for k, f in pairs(Spy.frames) do
      Spy.SaveFrame(f)
   end
end)

Spy.AddStartupFunc(function()
   -- Spy.CreateManagedFrame('frame', 'SpyPanelSW', 100, 100, false, false)
   -- Spy.CreateManagedFrame('frame', 'SpyPanelSE', 100, 100, false, false)
   
   local function CreatePanel(name)
      local f = Spy.CreateBox(name, 400, 180, {0, 0, 0}, 0.5)
      f:SetFrameStrata('BACKGROUND')
      return f
   end
   
   local sw = CreatePanel('SpyPanelSW')   
   sw:SetPoint('BOTTOMLEFT')   
   if ChatFrame1ResizeTopRight and ChatFrame1:IsVisible() then
      SpyPanelSW:SetPoint('TOPRIGHT', ChatFrame1ResizeTopRight)
   end
   
   -- local se = CreatePanel('SpyPanelSE')
   -- se:SetPoint('BOTTOMRIGHT')
   -- if ChatFrame3ResizeTopLeft and ChatFrame3:IsVisible() then
      -- SpyPanelSE:SetPoint('TOPLEFT', ChatFrame3ResizeTopLeft)
   -- end
end)

-------------------------------------------------------------------------------
Spy.RegisterEvents = RegisterEvents
Spy.RegisterHandlers = RegisterHandlers

function Spy.RegisterMethods(obj, methods)
   if methods == nil then return end
   for k, v in pairs(methods) do
      assert(obj[k] == nil)
      obj[k] = v
   end
end

function Spy.RegisterAll(widget, events, handlers)
   Spy.RegisterEvents(widget, events)
   Spy.RegisterHandlers(widget, handlers)
end

function Spy.SetCommonEventHandler(widget, handler, ...)
   local args = {...}
   for i, v in ipairs(args) do
      widget:SetScript(v, handler)
   end
end

-------------------------------------------------------------------------------
SpyTextList = Spy.NewFrameType('SpyTextList', 'FRAME')

function SpyTextList.Create(name, parent, tfont, background, margin, listMargin)
   local self = Spy.CreateFrame('SpyTextList', name, parent)
   self:SetWidth(100)
   self:SetHeight(100)
   Spy.ManageFrame(self)
   MakeFrameDraggable(self, true)
   Spy.CreateBackground(self, {0, 0, 0}, 0.5)
   self:Show()
   -- local self = Spy.CreateManagedFrame("FRAME", name, 100, 100)
   self.tfont = tfont or "NumberFontNormalSmall"
   self.background = background
   self.margin = margin or 1
   self.listMargin = listMargin or 2
   self.entries = {}
   -- self.CreateEntry = SpyTextList.CreateEntry
   -- self.UpdateWidth = SpyTextList.UpdateWidth
   return self
end

function SpyTextList:SetEntry(index, text)
   local numEntries = #self.entries
   if index > numEntries then
      for i=numEntries, index do
         self:CreateEntry('-')
      end
   end
   
   local entry = self.entries[index]
   entry:SetText(text)
   self:UpdateWidth()
end

function SpyTextList:UpdateWidth(padding)
   local newWidth = 0
   for i, v in ipairs(self.entries) do
      local w = v.text:GetStringWidth()
      if w > newWidth then newWidth = w end
   end
   self:SetWidth(newWidth + (padding or 0) + 4)
end

function SpyTextList:CreateEntry(text, textColor, backColor, regMouse)
   local f = CreateFrame("BUTTON", nil, self)
   f:SetWidth(100)
   if regMouse then
      f:EnableMouse(true)
      f:SetScript("OnEnter", function(self)
         self.back:SetTexture(unpack(self.backColorHi))
         end)
      f:SetScript("OnLeave", function(self)
         self.back:SetTexture(unpack(self.backColor))
         end)
   else
      f:EnableMouse(false)
   end
   f.SetText = function(self, txt, ...)
      self.text:SetText(format(txt, ...))
      end
   
   f.back = f:CreateTexture(self.background, "BACKGROUND")
   f.back:SetAllPoints()
   f.backColor = backColor or {0, 0, 0, 0.5}
   f.backColorHi = {}
   for i=1, 3 do
      f.backColorHi[i] = min(f.backColor[i] + 0.2)
   end
   f.back:SetTexture(unpack(f.backColor))
   f.back:SetAlpha(f.backColor[4] or 1.0)
   f.back:Show()

   local s = f:CreateFontString(nil, "OVERLAY", self.tfont)
   f.text = s
   local fontY = select(2, s:GetFont()) + 2
   local entryY = round(fontY)
   f:SetHeight(entryY)
   s:SetJustifyH(self.justifyH or "LEFT")
   s:SetJustifyV(self.justifyV or "MIDDLE")
   s:SetAllPoints()
   s:SetText(text)
   if textColor then s:SetTextColor(unpack(textColor)) end
   
   tinsert(self.entries, f)
   local prev = nil
   local numEntries = #self.entries
   if numEntries > 1 then
      prev = self.entries[numEntries-1]
   end
   
   local fheight = round(f:GetHeight())
   if prev then
      f:SetPoint("TOPLEFT", prev, "BOTTOMLEFT", 0, -self.margin)
      f:SetPoint("BOTTOMLEFT", prev, "BOTTOMLEFT", 0, -(fheight + self.margin))
      f:SetPoint("TOPRIGHT", prev, "BOTTOMRIGHT", 0, -self.margin)
      f:SetPoint("BOTTOMRIGHT", prev, "BOTTOMRIGHT", 0, -(fheight + self.margin))
   else
      f:SetPoint("TOPLEFT", self, "TOPLEFT", self.listMargin, -self.listMargin)
      f:SetPoint("BOTTOMLEFT", self, "TOPLEFT", self.listMargin, -(fheight + self.listMargin))
      f:SetPoint("TOPRIGHT", self, "TOPRIGHT", -self.listMargin, -self.listMargin)
      f:SetPoint("BOTTOMRIGHT", self, "TOPRIGHT", -self.listMargin, -(fheight + self.listMargin))
   end
   -- f:SetPoint("RIGHT", self, "RIGHT", -self.listMargin, 0)
   
   self:SetHeight(round(self:GetTop() - f:GetBottom()) + self.listMargin)
   return f
end

-------------------------------------------------------------------------------
function Spy.CreateStatusBar(name, parent, color, backColor, backAlpha)
   local bar = CreateFrame("STATUSBAR", name, parent, "SpyHudStatusBarTemplate")
   bar:SetStatusBarTexture(SpyDB.HealthBar.Texture)
   bar:SetMinMaxValues(0, 100)

   if color then
      bar:SetStatusBarColor(unpack(color))
   else
      bar:SetStatusBarColor(1, 1, 1)
   end

   bar.txt = bar:CreateFontString("$parent_Text", "OVERLAY", "NumberFontNormalSmall")
   bar.txt:SetAllPoints()
   bar.txt:SetText("Hi")
   bar.txt:SetTextColor(1, 1, 1)
   bar.txt:SetDrawLayer("OVERLAY")
   bar.txt:Show()
   
   if backColor == nil then backColor = {0, 0, 0} end
   if backAlpha == nil then backAlpha = 1.0 end
   
   bar.texture = bar:CreateTexture(nil, "BACKGROUND")
   bar.texture:SetAllPoints(bar)
   bar.texture:SetTexture(unpack(backColor))
   bar.texture:SetAlpha(backAlpha)
   bar:Show()
   
   bar.curValue = 0
   return bar
end

function Spy.CreateLayeredBar(name, parent, bVertical)
   if not parent then parent=UIParent end
   
   local bar = CreateFrame("BUTTON", name, parent)
   bar:SetWidth(SpyDB.HealthBar.Height)
   bar:SetHeight(SpyDB.HealthBar.Height)
   bar:SetAlpha(1.0)
   
   bar.texture = bar:CreateTexture(nil, "BACKGROUND")
   bar.texture:SetAllPoints(bar)
   bar.texture:SetTexture(0, 0, 0)
   bar.texture:SetAlpha(1.0)

   bar.bar = CreateFrame("STATUSBAR", name.."_bar", bar)
   bar.bar:SetStatusBarTexture(SpyDB.HealthBar.Texture)
   bar.bar:SetMinMaxValues(0, 100)
   bar.bar:SetStatusBarColor(0, 0, 0.5)
   bar.bar:SetAllPoints()
   bar.bar:SetAlpha(1.0)

   bar.predict = CreateFrame("STATUSBAR", name.."_predict", bar)
   bar.predict:SetStatusBarTexture(SpyDB.HealthBar.Texture)
   bar.predict:SetMinMaxValues(0, 100)
   bar.predict:SetStatusBarColor(0, 0, 1)
   bar.predict:SetAllPoints()
   bar.predict:SetAlpha(0.7)
   bar.predict:SetFrameLevel(bar.bar:GetFrameLevel() + 1)
   
   if bVertical then
      bar.bar:SetOrientation("VERTICAL")
      bar.predict:SetOrientation("VERTICAL")
      bar.bar:SetRotatesTexture(true)
      bar.predict:SetRotatesTexture(true)
   end
   
   bar.txtFrame = CreateFrame("FRAME", name.."_txtFrame", bar)
   bar.txtFrame:SetFrameLevel(bar.predict:GetFrameLevel() + 1)
   bar.txtFrame:SetAllPoints()
   bar.txtFrame:SetAlpha(1.0)
   bar.txtFrame:Show()
   
   bar.txt = bar.txtFrame:CreateFontString("$parent_Text", "OVERLAY", "NumberFontNormalSmall")
   bar.txt:SetAllPoints()
   bar.txt:SetText("")
   bar.txt:SetTextColor(1, 1, 1)
   bar.txt:Show()

   bar.predictOffset = 0
   bar.bUpdate = true
   
   function bar:Init(lo, hi)
      self.curValue = -1
      self.bUpdate = true
      self.bar:SetMinMaxValues(lo, hi)
      self.predict:SetMinMaxValues(lo, hi)
   end
   
   function bar:Update(x, y)
      if self.GetValueFunc then
         self.curValue = self:GetValueFunc()
      end
      if self.curValue ~= self.bar:GetValue() or self.bUpdate then
         self.bUpdate = false
         self.bar:SetValue(self.curValue)
         self.predict:SetValue(self.curValue + self.predictOffset)
         if self.GetUpdatedText then
            self.txt:SetText(self:GetUpdatedText())
         end
      end
   end
   
   function bar:GetUpdatedText()
      local min, max = self.bar:GetMinMaxValues()
      return format("%d / %d", self.bar:GetValue(), max)
   end
   
   function bar:SetPredict(offset)
      self.predictOffset = offset
      self.bUpdate = true
   end
   
   bar.GetValueFunc = nil
   bar:SetScript("OnUpdate", bar.Update)
   bar:Show()
   return bar
end

function Spy.CreateGenericWindow()
   local f = CreateFrame("Frame")
   f:ClearAllPoints()
   f:SetBackdrop(StaticPopup1:GetBackdrop())
   f:SetHeight(300)
   f:SetWidth(300)

   f.text = f:CreateFontString(nil, "BACKGROUND", "GameFontNormal")
   f.text:SetAllPoints()
   f.text:SetText("Lizards like to eat snorkels")
   f:SetPoint("CENTER", 0, 0)
   return f
end

function Spy.CreateButton(text, handler, parent, name)
   local b = CreateFrame('BUTTON', nil, parent or UIParent, 'OptionsButtonTemplate')
   b:SetText(text or ' ')
   b:SetWidth(b:GetTextWidth() + 10)
   if handler then
      b:SetScript('OnClick', handler)
   end
   return b
end

-------------------------------------------------------------------------------
function Spy.PrintAllSpells()
   local i = 1
   while true do
      local spell, rank = GetSpellName(i, BOOKTYPE_SPELL)
      if (not spell) then
         break
      end
      if (rank) then
         spell = spell.." ("..rank..")"
      end
      DEFAULT_CHAT_FRAME:AddMessage(i..": "..spell)
      i = i + 1
   end
end

